//
// Created by user on 2022/3/28.
//

#include "test_runner.h"

#include <utility>

bool Dis::test_runner::run(int cnt) {
    bool rst = true;

    // 备份cin、cout原始指针
    std::streambuf *old_in = std::cin.rdbuf();
    std::streambuf *old_out = std::cout.rdbuf();

    // 执行开始结束时间
    clock_t start_t, end_t;

    for (int i = 0; cnt == -1 || i < cnt; i++) {
        std::cout << "Test #" << i << ": " << std::endl;

        std::string test_data_file_name = get_test_data_file_name(i);
        std::string std_answer_file_name = get_std_answer_file_name(i);
        std::string sol_answer_file_name = get_solve_answer_file_name(i);

        std::cout << "Generate test data." << std::endl;
        // 设置随机文件指针
        std::ofstream test_data_file_out(test_data_file_name);
        std::cout.rdbuf(test_data_file_out.rdbuf());
        // 生成测试数据
        test_data_fun_ptr();
        // 保存
        test_data_file_out.close();
        std::cout.rdbuf(old_out);

        std::cout << "Running standard function." << std::endl;
        // 运行标程
        std::ifstream test_data_file_in(test_data_file_name);
        std::ofstream std_answer_file_out(std_answer_file_name);
        std::cin.rdbuf(test_data_file_in.rdbuf());
        std::cout.rdbuf(std_answer_file_out.rdbuf());
        // 生成标程答案
        std_fun_ptr();
        // 保存
        test_data_file_in.close();
        std_answer_file_out.close();
        std::cin.rdbuf(old_in);
        std::cout.rdbuf(old_out);

        std::cout << "Running solve function." << std::endl;
        // 运行测试程序
        test_data_file_in.open(test_data_file_name);
        std::ofstream sol_answer_file_out(sol_answer_file_name);
        std::cin.rdbuf(test_data_file_in.rdbuf());
        std::cout.rdbuf(sol_answer_file_out.rdbuf());
        // 生成测试答案
        start_t = clock();
        solve_fun_ptr();
        end_t = clock();
        // 保存
        test_data_file_in.close();
        sol_answer_file_out.close();
        std::cin.rdbuf(old_in);
        std::cout.rdbuf(old_out);

        // 对比答案
        std::stringstream ss("");
        ss << "fc \"" << std_answer_file_name << "\" \"" << sol_answer_file_name << "\"";
        if (!system(ss.str().c_str())) {
            std::cout << "Accept";
            if (show_exec_time) std::cout << "(Exec Time: " << end_t - start_t << "ms)";
            std::cout << std::endl;
        } else {
            std::cout << "Wrong Answer" << std::endl;
            rst = false;
            break;
        }

        // 恢复
        std::cin.rdbuf(old_in);
        std::cout.rdbuf(old_out);
    }

    return rst;
}

std::string Dis::test_runner::get_test_data_file_name(int i) {
    std::stringstream ss("");
    ss << save_path << '\\' << (test_point_pre.empty() ? DEFAULT_PRE_TEST_POINT : test_point_pre);
    if (use_id) ss << '_' << i;
    ss << SUFFIX_TEST_DATA;
    return ss.str();
}

std::string Dis::test_runner::get_std_answer_file_name(int i) {
    std::stringstream ss("");
    ss << save_path << '\\' << (test_point_pre.empty() ? DEFAULT_PRE_TEST_POINT : test_point_pre);
    if (use_id) ss << '_' << i;
    ss << SUFFIX_STD_ANSWER;
    return ss.str();
}

std::string Dis::test_runner::get_solve_answer_file_name(int i) {
    std::stringstream ss("");
    ss << save_path << '\\' << (test_point_pre.empty() ? DEFAULT_PRE_TEST_POINT : test_point_pre);
    if (use_id) ss << '_' << i;
    ss << SUFFIX_SOL_ANSWER;
    return ss.str();
}

Dis::test_runner::test_runner(void (*solveFunPtr)(), void (*stdFunPtr)(), void (*testDataFunPtr)(), std::string _save_path) :
solve_fun_ptr(solveFunPtr), std_fun_ptr(stdFunPtr), test_data_fun_ptr(testDataFunPtr),
save_path(std::move(_save_path)), test_point_pre(DEFAULT_PRE_TEST_POINT), use_id(true), show_exec_time(true) {}

const std::string &Dis::test_runner::get_test_point_pre() const {
    return test_point_pre;
}

void Dis::test_runner::set_test_point_pre(const std::string &testPointPre) {
    test_point_pre = testPointPre;
}

bool Dis::test_runner::is_use_id() const {
    return use_id;
}

void Dis::test_runner::set_use_id(bool useId) {
    use_id = useId;
}

bool Dis::test_runner::is_show_exec_time() const {
    return show_exec_time;
}

void Dis::test_runner::set_show_exec_time(bool showExecTime) {
    this -> show_exec_time = showExecTime;
}
